Understanding Extension Methods

.NET 3.5 introduced the concept of extension methods, which allow you to tack on new functionality to precompiled types on the fly. As you know, once a type is defined and compiled into a .NET assembly, its definition is, more or less, final. The only way to add new members, update members, or remove members is to recode and recompile the code base into an updated assembly (or take more drastic measures, such as using the System.Reflection.Emit namespace to dynamically reshape a compiled type in memory).

In C#, it is now possible to define extension methods. In a nutshell, extension methods allow existing compiled types (specifically, classes, structures, or interface implementations) as well as types currently being compiled (such as types in a project that contains extension methods) to gain new functionality without needing to directly update the type being extended.

This technique can be quite helpful when you need to inject new functionality into types for which you do not have an existing code base. It can also be handy when you need to force a type to support a set of members (in the interest of polymorphism), but cannot modify the original type declaration. Using extension methods, you can add functionality to precompiled types while providing the illusion these methods were there all along.

Note Understand that extension methods do not literally change the compiled code base! This technique only adds members to a type within the context of the current application.

When you define extension methods, the first restriction is that they must be defined within a static class (see Chapter 5), and therefore each extension method must be declared with the static keyword. The second point is that all extension methods are marked as such by using the this keyword as a modifier on the first (and only the first) parameter of the method in question. The third point is that every extension method can be called either from the correct instance in memory or statically via the defining static class! Sound strange? Let’s look at a full example to clarify matters.

Defining Extension Methods

Create a new Console Application named ExtensionMethods. Now, assume you are authoring a utility class named MyExtensions that defines two extension methods. The first method allows any object in the .NET base class libraries to have a brand-new method named DisplayDefiningAssembly() that makes use of types in the System.Reflection namespace to display the assembly of the specified type.

Note You will formally examine the reflection API in Chapter 15. If you are new to the topic, simply understand that reflection allows you to discover the structure of assemblies, types, and type members at runtime.

The second extension method, named ReverseDigits(), allows any System.Int32 to obtain a new version of itself where the value is reversed digit by digit. For example, if an integer with the value 1234 called ReverseDigits(), the integer returned is set to the value 4321. Consider the following class implementation (be sure to import the System.Reflection namespace if you are following along):

static class MyExtensions
{
    // This method allows any object to display the assembly
    // it is defined in.
    public static void DisplayDefiningAssembly(this object obj)
    {
        Console.WriteLine("{0} lives here: => {1}\n", obj.GetType().Name,
            Assembly.GetAssembly(obj.GetType()).GetName().Name);
    }

    // This method allows any integer to reverse its digits.
    // For example, 56 would return 65.
    public static int ReverseDigits(this int i)
    {
        // Translate int into a string, and then
        // get all the characters.
        char[] digits = i.ToString().ToCharArray();

        // Now reverse items in the array.
        Array.Reverse(digits);
    
        // Put back into string.
        string newDigits = new string(digits);

        // Finally, return the modified string back as an int.
        return int.Parse(newDigits);
    }
}

Again, note how the first parameter of each extension method has been qualified with the this keyword, before defining the parameter type. It is always the case that the first parameter of an extension method represents the type being extended. Given that DisplayDefiningAssembly() has been prototyped to extend System.Object, any type in any assembly now has this new member. However, ReverseDigits() has been prototyped to only extend integer types, and therefore if anything other than an integer attempts to invoke this method, you will receive a compile-time error.

Understand that a given extension method can have multiple parameters, but only the first parameter can be qualified with this. For example, here is an overloaded extension method defined in another utility class named TesterUtilClass:

static class TesterUtilClass
{
    // Every Int32 now has a Foo() method...
    public static void Foo(this int i)
    { Console.WriteLine("{0} called the Foo() method.", i); }

    // ...which has been overloaded to take a string!
    public static void Foo(this int i, string msg)
    { Console.WriteLine("{0} called Foo() and told me: {1}", i, msg); }
}

Invoking Extension Methods on an Instance Level

Now that we have these extension methods, look at how all objects (which of course means everything in the .NET base class libraries) have a new method named DisplayDefiningAssembly(), while System.Int32 types (and only integers) have methods named ReverseDigits() and Foo():

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Extension Methods *****\n");
    
    // The int has assumed a new identity!
    int myInt = 12345678;
    myInt.DisplayDefiningAssembly();
    
    // So has the DataSet!
    System.Data.DataSet d = new System.Data.DataSet();
    d.DisplayDefiningAssembly();
    
    // And the SoundPlayer!
    System.Media.SoundPlayer sp = new System.Media.SoundPlayer();
    sp.DisplayDefiningAssembly();
    
    // Use new integer functionality.
    Console.WriteLine("Value of myInt: {0}", myInt);
    Console.WriteLine("Reversed digits of myInt: {0}", myInt.ReverseDigits());
    myInt.Foo();
    myInt.Foo("Ints that Foo? Who would have thought it!");
    
    bool b2 = true;
    
    // Error! Booleans don't have the Foo() method!
    // b2.Foo();
    
    Console.ReadLine();
}

Here is the output.

***** Fun with Extension Methods *****

Int32 lives here: => mscorlib

DataSet lives here: => System.Data

SoundPlayer lives here: => System

Value of myInt: 12345678
Reversed digits of myInt: 87654321
12345678 called the Foo() method.
12345678 called Foo() and told me: Ints that Foo? Who would have thought it!

Invoking Extension Methods Statically

Recall that the first parameter of an extension method is marked with the this keyword, followed by the type of item the method is applicable to. If you peek at what is happening behind the scenes (as verified by a tool such as ildasm.exe), you will find that the compiler simply calls the “normal” static method, passing in the variable calling the method as a parameter (e.g., it is the value of this). Consider the following C# code, which approximates the code substitution that took place:

private static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Extension Methods *****\n");
    int myInt = 12345678;
    MyExtensions.DisplayDefiningAssembly(myInt);
    
    System.Data.DataSet d = new DataSet();
    MyExtensions.DisplayDefiningAssembly(d);
    
    System.Media.SoundPlayer sp = new SoundPlayer();
    MyExtensions.DisplayDefiningAssembly(sp);
    
    Console.WriteLine("Value of myInt: {0}", myInt);
    Console.WriteLine("Reversed digits of myInt: {0}",
        MyExtensions.ReverseDigits(myInt));
    
    TesterUtilClass.Foo(myInt);
    TesterUtilClass.Foo(myInt, "Ints that Foo? Who would have thought it!");
    
    Console.ReadLine();
}

Given that calling an extension method from an object (thereby making it seem that the method is in fact an instance-level method) is just some smoke-and-mirrors effect provided by the compiler, you are always free to call extension methods as normal static methods using the expected C# syntax (as just shown).

The Scope of an Extension Method

As just explained, extension methods are essentially static methods that can be invoked from an instance of the extended type. Given this flavor of syntactic sugar, it is really important to point out that unlike a “normal” method, extension methods do not have direct access to the members of the type they are extending; said another way, extending is not inheriting. Consider the following simple Car type:

public class Car
{
    public int Speed;
    public int SpeedUp()
    {
        return ++Speed;
    }
}

If you were to build an extension method for the Car type named SlowDown(), you would not have direct access to the members of Car within the scope of the extension method as this is not classical inheritance. Therefore, the following would result in a compiler error:

public static class CarExtensions
{
    public static int SlowDown(this Car c)
    {
        // Error! This method is not deriving from Car!
        return --Speed;
    }    
}

The problem here is that the static SlowDown() extension method is attempting to access the Speed field of the Car type. However, because SlowDown() is a static member of the CarExtensions class, Speed does not exist in this context! What is permissible, however, is to make use of the this-qualified parameter to access all public members (and only the public members) of the type being extending. Thus, the following code compiles as expected:

public static class CarExtensions
{
    public static int SlowDown(this Car c)
    {
        // OK!
        return --c.Speed;
    }
}

At this point, you could create a Car object and invoke the SpeedUp() and SlowDown() methods as follows:

static void UseCar()
{
    Car c = new Car();
    Console.WriteLine("Speed: {0}", c.SpeedUp());
    Console.WriteLine("Speed: {0}", c.SlowDown());
}

Importing Types That Define Extension Methods

When you partition a set of static classes containing extension methods in a unique namespace, other namespaces in that assembly will make use of the standard C# using keyword to import not only the static classes themselves, but also each of the supported extension methods. This is important to remember, because if you do not explicitly import the correct namespace, the extension methods are not available for that C# code file.

In effect, although it can appear on the surface that extension methods are global in nature, they are in fact limited to the namespaces that define them or the namespaces that import them. Thus, if we wrap the definitions of our static classes (MyExtensions, TesterUtilClass, and CarExtensions) into a namespace named MyExtensionMethods as follows:

namespace MyExtensionMethods
{
    static class MyExtensions
    {
        ...
    }

    static class TesterUtilClass
    {
        ...
    }

    static class CarExtensions
    {
        ...
    }
}

other namespaces in the project would need to explicitly import the MyExtensionMethods namespace to gain the extension methods defined by these types. Therefore, the following is a compiler error:

// Here is our only using directive.
using System;

namespace MyNewApp
{
    class JustATest
    {    
        void SomeMethod()
        {
            // Error! Need to import MyExtensionMethods
            // namespace to extend int with Foo()!
            int i = 0;
            i.Foo();
        }
    }
}

The IntelliSense of Extension Methods

Given the fact that extension methods are not literally defined on the type being extended, it is certainly possible to become confused when examining an existing code base. For example, assume you have imported a namespace that defined some number of extension methods authored by a teammate. As you are authoring your code, you might create a variable of the extended type, apply the dot operator, and find dozens of new methods that are not members of the original class definition!

Thankfully, Visual Studio’s IntelliSense mechanism marks all extension methods with a unique, blue down-arrow icon (see Figure 12-5).

Figure 12-5. The IntelliSense of extension methods

Any method marked with this visual icon is a friendly reminder that the method is defined outside of the original class definition via an extension method.

Building and Using Extension Libraries

The previous example extended the functionality of various types (such as the System.Int32 type) for use by the current console application. However, I am sure you can imagine the usefulness of building a.NET code library that defines extensions that can be referenced by multiple applications. As luck would have it, doing so is very straightforward.

Chapter 14 will delve into the details of creating and configuring custom libraries; however, if you wish to follow along for this example, create a new Class Library project named MyExtensionsLibrary. Next, rename your initial C# code file to MyExtensions.cs and copy the MyExtensions class definition in your new namespace:

namespace MyExtensionsLibrary
{
    // Be sure to import System.Reflection.
    public static class MyExtensions
    {
        // Same implementation as before.
        public static void DisplayDefiningAssembly(this object obj)
        {...}

        // Same implementation as before.
        public static int ReverseDigits(this int i)
        {...}
    }
}

Note If you wish to export extension methods from a .NET code library, the defining type must be declared with the public keyword (recall that the default access modifier for a type is internal).

At this point, you can compile your library and reference the MyExtensionsLibrary.dll assembly within new .NET projects. When you do so, the new functionality provided to System.Object and System.Int32 can be used by any application that references the library.

To test this out, add a new Console Application project (named MyExtensionsLibraryClient). Next, add a reference to the MyExtensionsLibrary.dll assembly. Within the initial code file, specify that you are using the MyExtensionsLibrary namespace and author some simple code that invokes these new methods on a local integer:

using System;

// Import our custom namespace.
using MyExtensionsLibrary;

namespace MyExtensionsLibraryClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***** Using Library with Extensions *****\n");
            // This time, these extension methods
            // have been defined within an external
            // .NET class library.
            int myInt = 987;
            myInt.DisplayDefiningAssembly();
            Console.WriteLine("{0} is reversed to {1}",
                myInt, myInt.ReverseDigits());
            Console.ReadLine();
        }
    }
}

Microsoft recommends placing types that have extension methods in a dedicated assembly (within a dedicated namespace). The reason is simply to reduce cluttering of your programming environment. By way of example, if you were to author a core library for your company that every application was expected to make use of, and if the root namespace of that library defined 30 extension methods, the end result would be that all applications would see these methods pop up in IntelliSense (even if they are not required).

Source Code The MyExtensionsLibrary and MyExtensionsLibraryClient projects can be found under the Chapter 12 subdirectory.

Extending Interface Types via Extension Methods

At this point, you have seen how to extend classes (and, indirectly, structures that follow the same syntax) with new functionality via extension methods. To wrap up our investigation of C# extension methods, allow me to point out that it is possible to extend an interface type with new methods; however, the semantics of such an action are sure to be a bit different from what you might expect.

Create a new Console Application named InterfaceExtensions and define a simple interface type (IBasicMath) that contains a single method named Add(). Next, implement this interface on a class type (MyCalc) in a fitting manner, for example:

// Define a normal interface in C#.
interface IBasicMath
{
    int Add(int x, int y);
}

// Implementation of IBasicMath.
class MyCalc : IBasicMath
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}

Now, assume you do not have access to the code definition of IBasicMath, but wish to add a new member (such as a subtraction method) to expand its behavior. You might attempt to author the following extension class to do so:

static class MathExtensions
{
    // Extend IBasicMath with subtraction method?
    public static int Subtract(this IBasicMath itf,
        int x, int y);
}

However, this will result in compile-time errors. When you extend an interface with new members, you must also supply an implementation of these members! This seems to fly in the face of the very nature of interface types, as interfaces do not provide implementations, only definitions. Nevertheless, you are required to define your MathExtensions class as follows:

static class MathExtensions
{
    // Extend IBasicMath this method and this
    // implementation.
    public static int Subtract(this IBasicMath itf, int x, int y)
    {
        return x - y;
    }
}

At this point, you might assume you could create a variable of type IBasicMath and directly invoke Subtract(). Again, if this were possible (which it is not), it would destroy the nature of .NET interface types. In reality, what we have actually said here is “Any class in my project implementing IBasicMath now has a Subtract() method, implemented in this manner.” As before, all the basic rules apply, therefore the namespace defining MyCalc must have access to the namespace defining MathExtensions. Consider the following Main() method:

static void Main(string[] args)
{
    Console.WriteLine("***** Extending an interface *****\n");

    // Call IBasicMath members from MyCalc object.
    MyCalc c = new MyCalc();
    Console.WriteLine("1 + 2 = {0}", c.Add(1, 2));
    Console.WriteLine("1 - 2 = {0}", c.Subtract(1, 2));

    // Can also cast into IBasicMath to invoke extension.
    Console.WriteLine("30 - 9 = {0}", ((IBasicMath)c).Subtract(30, 9));

    // This would NOT work!
    // IBasicMath itfBM = new IBasicMath();
    // itfBM.Subtract(10, 10);

    Console.ReadLine();
}

That wraps up our examination of C# extension methods. Remember that this particular language feature can be very useful whenever you wish to extend the functionality of a type, even if you do not have access to the original source code (or if the type is sealed), for the purposes of polymorphism. And, much like implicitly typed local variables, extension methods are a key element of working with the LINQ API. As you will see in the next chapter, numerous existing types in the base class libraries have been extended with new functionality, via extension methods, to allow them to integrate with the LINQ programming model.

Source Code The InterfaceExtension project can be found under the Chapter 12 subdirectory.